import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import * as d3 from 'd3';

import {
	setInfo,
	demo6,
	removeNode
} from '../../action';

const NODE_SIZE = 40;

// import '../../style.less';
import dataCenter from '../../dataCenter';

const _ = require('lodash');

class SearchResult extends Component {
	constructor(props, context) {
		super(props);
		this.state = {
			nodeEvent: '',
			currentNodeId: '',
			dragNodeIdArray: [],
			dragRelationshipIdArray: [],
			svgWidth: 0
		};
		this.dragNodeIdArray = [];
		this.dragRelationshipIdArray = [];
		this.init = this.init.bind(this);
		this.resize = this.resize.bind(this);
		this.renderSimulation = this.renderSimulation.bind(this);
	}
	static contextTypes = {
    getSearchType: React.PropTypes.func.isRequired,
  };

	componentDidMount() {
		let s = ReactDOM.findDOMNode(this.refs.space).getBoundingClientRect();
		this.init();
		this.renderSimulation(this.props.nodes, this.props.relationships);
		console.log('s: ', s)
		this.setState({
			svgWidth: s.width
		})
	}

	shouldComponentUpdate(nextProps, nextState) {
		if (this.props.nodes !== nextProps.nodes || this.props.relationships !== nextProps.relationships) {
			this.renderSimulation(nextProps.nodes, nextProps.relationships);
		}
		if (this.state !== nextState) {
			return true
		}
		return false;
	}

	init() {
		const self = this;
		this.svg = d3.select(this.refs.svg)
			.on('click', this.disSelectNode.bind(this));
		this.container = this.svg.append('g')
			.attr('id', 'container')
			.attr('transform', 'translate(0, 0)')

		this.simulation = d3.forceSimulation()
			.force('link', d3.forceLink().id(d => d.id).distance(100))
			.force('charge', d3.forceManyBody().strength(-200))
			// .force('y', d3.forceY(100))
  	// 	.force('x', d3.forceX(100))
			// .force('collide', d3.forceCollide().radius(60).strength(1))
			.on('tick', this.ticked.bind(this))

		this.color = d3.scaleOrdinal().range(d3.schemeCategory20);
		dataCenter.getLabels()
			.then(labels => {
				self.color.domain(labels);
				self.renderLegend(labels);
		  })
		// render all detail here
		// dataCenter.getAllNodesCount()
		// 	.then(resp => {
		// 		console.log('resp: ', resp)
		// 	})
		
		this.resize();
		window.addEventListener('resize', this.resize.bind(this));
		this.nodeDrag = d3.drag()
			.on('start', (d) => {
				if (!d3.event.active) this.simulation.alphaTarget(0.3).restart();
			  d.fx = d3.event.x;
  			d.fy = d3.event.y;
  			this.initStateForDrag();
			})
			.on('drag', (d) => {
				// if (!d3.event.active) this.simulation.alphaTarget(0.3).stop();
				// d.x = d3.event.x;
				// d.y = d3.event.y;
				d.fx = d3.event.x;
  			d.fy = d3.event.y;
  			d.hasSelected = true;
  			this.setTargetNodeUnfix(d);
			})
			.on('end', (d) => {
				if (!d3.event.active) this.simulation.alphaTarget(0);
			  d.fx = d3.event.x;
			  d.fy = d3.event.y;
			  this.setTargetNodeFix(d);
			})

		this.zoom = d3.zoom()
			.scaleExtent([0.1, 4])
			.translateExtent([[-2000, -1000], [this.width+2000, this.height+900]])
			.on('zoom', () => {
				this.container.attr("transform", d3.event.transform);
			})
		this.svg.call(this.zoom);
		
	}
	initStateForDrag = () => {
		this.dragNodeIdArray = [];
		this.dragRelationshipIdArray = [];
	}
	setTargetNodeUnfix = (d) => {
		// console.log('d: ', d);
		let dragNodeIdArray = _.cloneDeep(this.dragNodeIdArray);
		let dragRelationshipIdArray = _.cloneDeep(this.dragRelationshipIdArray);
		const relationships = this.props.relationships.filter(item => item.startNode === d.id || item.endNode === d.id);
		this.dragNodeIdArray = _.uniq([...this.dragNodeIdArray, d.id]);
		relationships.map(item => {
			// console.log('dragRelationshipIdArray: ', dragRelationshipIdArray);
			// console.log('item.id: ', item.id);
			if (dragRelationshipIdArray.indexOf(item.id) === -1) {
				if (this.dragNodeIdArray.indexOf(item.startNode) === -1) {
					const startNodeDragForData = this.props.nodes.find(v => v.id === item.startNode);
					if (!startNodeDragForData.hasSelected) {
						startNodeDragForData.fx = null;
						startNodeDragForData.fy = null;
					} else {
						startNodeDragForData.fx = startNodeDragForData.x;
						startNodeDragForData.fy = startNodeDragForData.y;
					}
					// console.log('come here');
					this.dragRelationshipIdArray = _.uniq([...this.dragRelationshipIdArray, ...(relationships.map(item => item.id))]);
					this.setTargetNodeUnfix(startNodeDragForData);
				} else if (this.dragNodeIdArray.indexOf(item.endNode) === -1) {
					const endNodeDragForData = this.props.nodes.find(v => v.id === item.endNode);
					if (!endNodeDragForData.hasSelected) {
						endNodeDragForData.fx = null;
						endNodeDragForData.fy = null;
					} else {
						endNodeDragForData.fx = endNodeDragForData.x;
						endNodeDragForData.fy = endNodeDragForData.y;
					}
					// console.log('come here');
					this.dragRelationshipIdArray = _.uniq([...this.dragRelationshipIdArray, ...(relationships.map(item => item.id))]);
					this.setTargetNodeUnfix(endNodeDragForData);
				}
			}
			
		})
	}
	setTargetNodeFix = (d) => {
		this.dragNodeIdArray.map(item => {
			const resetNode = this.props.nodes.find(v => v.id === item);
			resetNode.fx = resetNode.x;
			resetNode.fy = resetNode.y;
		})
	}

	renderLegend(labels) {
		let container = this.svg.append('g')
			.classed('legendContainer', true)
			.attr('transform', `translate(${30},${10})`)
			
		let items = container.selectAll('g.legend')
			.data(labels)
			.enter()
			.append('g')
			.classed('legend', true)
			.attr('transform', (d, i) => `translate(${10 + i * 120}, ${10})`)

		items.append('circle')
			.attr('r', 10)
			.attr('fill', d => this.color(d))

		items.append('text')
			.attr('text-anchor', 'start')
			.attr('x', 20)
			.attr('y', 5)
			.text(d => d)


	}

	renderSimulation(nodesData, relationships) {
		// console.log('nodesData: ', _.cloneDeep(nodesData));
		// console.log('relationships: ', _.cloneDeep(relationships));
		let self = this;
		nodesData = _.uniqBy(nodesData, 'id');
		relationships = _.uniqBy(relationships, 'id');
		let links_update = this.container.selectAll('g.link')
			.data(relationships, d => d.id)
		links_update.exit()
			.remove()

		let links = links_update.enter()
			.append('g')
			.classed('link', true)
		links.append('line')
			.attr('d', d => this._make_line(d))
			.style('stroke', 'rgb(64, 64, 64)')
    	.style('stroke-width', 2)
    	.attr('marker-end', 'url(#arrow)')
    links.append('text')
	    .attr('text-anchor', 'middle')
	    .attr('fill', 'black')
	    .attr('font-size', 10)
	    .text(d => d.type)
	  this.links = links.merge(links_update);

	  let nodes_update = this.container.selectAll('g.node')
	  	.data(nodesData, d => d.id)

	  nodes_update.exit()
	  	.remove()
		let nodes = nodes_update.enter()
			.append('g')
			.classed('node', true)
			.attr('id', d => d.id)
			.call(this.nodeDrag)
			.on('click', this.selectNode.bind(this))
			.on('mouseover', this.havorNode.bind(this))
			.on('mouseout', this.outNode.bind(this))
		nodes.append('circle')
     .attr('r', 30)
     .attr('fill', d => this.color(d.labels[0]))
     .attr('stroke', 'black')
     .attr('stroke-width', 1)

		nodes.append('text')
		  .attr('text-anchor', 'middle')
		  .attr('fill', 'white')
		  .attr('y', 5)
		  .text(d => d.id)

		const arcGeneratorDel = d3.arc()
    	.innerRadius(40)
    	.outerRadius(70)
    	.startAngle(0)
			.endAngle((2 / 3) * Math.PI)
			.padAngle(.02)
			.padRadius(100)
			.cornerRadius(4)

		const arcGeneratorExpand = d3.arc()
    	.innerRadius(40)
    	.outerRadius(70)
    	.startAngle((2 / 3) * Math.PI)
			.endAngle((4 / 3) * Math.PI)
			.padAngle(.02)
			.padRadius(100)
			.cornerRadius(4)

		const arcGeneratorLock = d3.arc()
    	.innerRadius(40)
    	.outerRadius(70)
    	.startAngle((4 / 3) * Math.PI)
			.endAngle(2 * Math.PI)
			.padAngle(0.02)
			.padRadius(100)
			.cornerRadius(4)

		const arcGeneratorRound = d3.arc()
    	.innerRadius(30)
    	.outerRadius(40)
    	.startAngle(0)
			.endAngle(2 * Math.PI)

		nodes.append('path')
			.attr('d', arcGeneratorRound)
			.attr('fill', '#00a8542e')
			.classed('activePath', true)
			.style('display', 'none')

		nodes.append('path')
			.attr('d', arcGeneratorDel)
			.attr('fill', '#666666')
			.classed('arcPath', true)
			.style('cursor', 'pointer')
			.style('display', 'none')
			.on('click', this.removeCurrentNode.bind(this))

		nodes.append('text')
		  .attr('text-anchor', 'middle')
		  .attr('x', () => {
		  	return arcGeneratorDel.centroid({
		  		startAngle: 0,
		  		endAngle: (2 / 3) * Math.PI
		  	})[0]
		  })
		  .attr('y', () => {
		  	return arcGeneratorDel.centroid({
		  		startAngle: 0,
		  		endAngle: (2 / 3) * Math.PI
		  	})[1]
		  })
		  .attr('dy', '0.33em')
		  .style('cursor', 'pointer')
		  .style('display', 'none')
		  .classed('expand', true)
		  .text(d => '删除')
		  .on('click', this.removeCurrentNode.bind(this))

		nodes.append('path')
			.attr('d', arcGeneratorExpand)
			.attr('fill', '#d2eafb')
			.classed('arcPath', true)
			.style('cursor', 'pointer')
			.style('display', 'none')
			.on('click', this.expandCurrentNode.bind(this))

		nodes.append('text')
		  .attr('text-anchor', 'middle')
		  .attr('x', () => {
		  	return arcGeneratorExpand.centroid({
		  		startAngle: (2 / 3) * Math.PI,
		  		endAngle: (4 / 3) * Math.PI
		  	})[0]
		  })
		  .attr('y', () => {
		  	return arcGeneratorExpand.centroid({
		  		startAngle: (2 / 3) * Math.PI,
		  		endAngle: (4 / 3) * Math.PI
		  	})[1]
		  })
		  .attr('dy', '0.33em')
		  .style('cursor', 'pointer')
		  .style('display', 'none')
		  .classed('expand', true)
		  .text(d => '展开')
		  .on('click', this.expandCurrentNode.bind(this))

		nodes.append('path')
			.attr('d', arcGeneratorLock)
			.classed('arcPath', true)
			.attr('fill', '#fdefc6')
			.style('display', 'none')

		nodes.append('text')
		  .attr('text-anchor', 'middle')
		  .attr('x', () => {
		  	return arcGeneratorLock.centroid({
		  		startAngle: (4 / 3) * Math.PI,
		  		endAngle: 2 * Math.PI
		  	})[0]
		  })
		  .attr('y', () => {
		  	return arcGeneratorLock.centroid({
		  		startAngle: (4 / 3) * Math.PI,
		  		endAngle: 2 * Math.PI
		  	})[1]
		  })
		  .attr('dy', '0.33em')
		  .style('cursor', 'pointer')
		  .style('display', 'none')
		  .classed('expand', true)
		  .text(d => '锁定')
		  .on('click', this.lockCurrentNode.bind(this))
		  
		this.nodes = nodes.merge(nodes_update)
			.each(function() {
				// console.log('this.parentNode: ', this.parentNode)
		  	this.parentNode.appendChild(this);
		  })
		this.simulation
			.nodes(nodesData)
			
		this.simulation.force("link")
      .links(relationships)
    this.simulation.alphaTarget(1).restart();

    if (this.context.getSearchType() === 'node') {
    	setTimeout(() => {
    		this.nodes.each(function(d) {
    			d.fx = d.x;
    			d.fy = d.y
    		})
    		this.simulation.stop();
    	}, 1000)
    }
	}

	_make_line(d) {
		console.log('d: ', d)
		let line = d3.line()
			.x(d => d[0])
			.y(d => d[1])
			.curve(d3.curveBundle);

		let data = [
			[d.source.x, d.source.y], 
			[d.source.x*3/4+d.target.x/4, d.source.y ],
			[d.source.x/2+d.target.x/2, d.source.y/2+d.target.y/2 ],
			[d.source.x/4+d.target.x*3/4, d.target.y ],
			[d.target.x, d.target.y]
		];
		return line(data)
	}

	expandCurrentNode = (d) => {
		d3.event.stopPropagation();
		this.props.dispatch(demo6(d.id));
	}
	removeCurrentNode = (d) => {
		d3.event.stopPropagation();
		this.props.dispatch(removeNode(d.id));
		this.simulation.alphaTarget(1).restart();
		if (this.context.getSearchType() === 'node') {
    	setTimeout(() => {
    		this.nodes.each(function(d) {
    			d.fx = d.x;
    			d.fy = d.y
    		})
    		this.simulation.stop();
    	}, 300)
    }
	}
	lockCurrentNode = (d) => {
		d3.selectAll('path.arcPath')
			.style('display', 'none')
		d3.selectAll('path.activePath')
			.style('display', 'none')
		d3.select('#container').selectAll('text.expand')
			.style('display', 'none')
		if (this.state.currentNodeId !== d.id) {
			d3.select(d3.event.target.parentNode).selectAll('path.arcPath')
				.style('display', 'block')
			d3.select(d3.event.target.parentNode).selectAll('path.activePath')
				.style('display', 'block')
			d3.select(d3.event.target.parentNode).selectAll('text.expand')
				.style('display', 'block')
		}
		this.setState({
			nodeEvent: this.state.nodeEvent === 'click' && this.state.currentNodeId === d.id ? '' : 'click',
			currentNodeId: d.id
		})
		d.hasSelected = true;
	}

	ticked() {
		this.nodes.attr('transform', d => `translate(${d.x},${d.y})`)
							.attr('vx', 1000)
							.attr('vy', 1000)

    this.links.select('line').attr('x1', d => d.source.x)
      .attr('y1', d => d.source.y)
      .attr('x2', d => d.target.x)
      .attr('y2', d => d.target.y)

    this.links.select('text')
      .attr('x', d => d.target.x/2+d.source.x/2)
      .attr('y', d => d.target.y/2+d.source.y/2)
	}

	selectNode(d) {
		d3.selectAll('path.arcPath')
			.style('display', 'none')
		d3.selectAll('path.activePath')
			.style('display', 'none')
		d3.select('#container').selectAll('text.expand')
			.style('display', 'none')
		if (this.state.currentNodeId !== d.id) {
			d3.select(d3.event.target.parentNode).selectAll('path.arcPath')
				.style('display', 'block')
			d3.select(d3.event.target.parentNode).selectAll('path.activePath')
				.style('display', 'block')
			d3.select(d3.event.target.parentNode).selectAll('text.expand')
				.style('display', 'block')
		}
		d3.event.stopPropagation();
		this.setState({
			nodeEvent: this.state.nodeEvent === 'click' && this.state.currentNodeId === d.id ? '' : 'click',
			currentNodeId: this.state.currentNodeId === d.id ? '' : d.id
		})
		this.props.dispatch(setInfo(d));
	}
	havorNode(d) {
		const activePath = d3.select(d3.event.target.parentNode).selectAll('path.activePath').style('display');
		const activeArcPath = d3.select(d3.event.target.parentNode).selectAll('path.arcPath').style('display');
		if (this.state.nodeEvent !== 'click') {
			d3.selectAll('path.activePath')
				.style('display', 'none')
			d3.select(d3.event.target.parentNode).selectAll('path.activePath')
				.style('display', 'block')
		}
	}
	outNode(d) {
		if (this.state.nodeEvent !== 'click') {
			d3.select(d3.event.target.parentNode).selectAll('path.activePath')
				.style('display', 'none')
		}
	}

	disSelectNode() {
		d3.selectAll('path.arcPath')
			.style('display', 'none')
		d3.selectAll('path.activePath')
			.style('display', 'none')
		d3.select(d3.event.target.parentNode).selectAll('text.expand')
			.style('display', 'none')
		this.setState({
			nodeEvent: '',
			currentNodeId: ''
		})
		this.props.dispatch(setInfo({}));
	}

	// displayName(data) {
	// 	let p_names = [/^name$/i, /^label$/i, /name$/i];
	// 	let keys = _.keys(data);

	// 	let scores = _.zipObject
	// 	_.chain(data)
	// 		.keys()
	// 		.
	// }

	resize() {
		let rect = ReactDOM.findDOMNode(this.refs.space).getBoundingClientRect();
		this.width = rect.width - 20;
		this.height = rect.height - 20;
		this.svg.attr('width', this.width)
			.attr('height', this.height)
		this.container.attr('width', this.width)
			.attr('height', this.height)

		this.simulation.force('center', d3.forceCenter(this.width/2, this.height/2))
	}

	render() {
		console.log('this.state.svgWidth: ', this.state.svgWidth);
		return <div ref="space" className="search-result thin-scrollbar">
			<svg ref="svg" id="searchResult">
				<line x1="0" y1="40" x2={`${this.state.svgWidth}`} y2="40" style={{stroke: 'rgb(238, 238, 238)', strokeWidth: 1}}/>
				<defs>
			    <marker
			      id="arrow"
			      markerUnits="strokeWidth"
			      markerWidth="12"
			      markerHeight="12"
			      viewBox="0 0 12 12"
			      refX="24"
			      refY="6"
			      orient="auto">
			      <path d="M2,2 L10,6 L2,10 L6,6 L2,2" style={{fill: "rgb(64, 64, 64)"}}></path>
			    </marker>
			  </defs>
			</svg>
		</div>;
	}
}

function mapState2Props(state) {
	let { graphData: {relationships, nodes} } = state;
	return { relationships, nodes };
}

SearchResult = connect(mapState2Props)(SearchResult);

export default SearchResult;